#pragma once
#include <iostream>
#include <string>
#include <cassert>
#include <unordered_map>
#include <functional>
#include "Epoller.hpp"
#include "Sock.hpp"
#include "Log.hpp"
#include "Util.hpp"
#include "Protocol.hpp"

using namespace protocol_ns;

const static int gport = 8888;
const static int bsize = 1024;
class Connection;
class EpollServer;

using func_t = std::function<void (Connection *, const Request&)>;
using callback_t = std::function<void(Connection *)>;

// 大号的结构体
class Connection
{
public:
    Connection(const int &fd, const std::string &clientip, const uint16_t &clientport)
        : fd_(fd), clientip_(clientip), clientport_(clientport_)
    {
    }
    void Register(callback_t recver, callback_t sender, callback_t excepter)
    {
        recver_ = recver;
        sender_ = sender;
        excepter_ = excepter;
    }
    ~Connection()
    {
    }

public:
    // IO信息
    int fd_;
    std::string inbuffer_;
    std::string outbuffer_;
    // IO处理函数
    callback_t recver_;
    callback_t sender_;
    callback_t excepter_;

    // 用户信息, only debug
    std::string clientip_;
    uint16_t clientport_;

    // 也可以给conn带上自己要关心的事件
    uint32_t events;

    // 回指指针
    EpollServer *R;
};

class EpollServer
{
    const static int gnum = 64;

public:
    EpollServer(func_t func, uint16_t port = gport) : func_(func), port_(port)
    {
    }

    void InitServer()
    {
        listensock_.Socket();
        listensock_.Bind(port_);
        listensock_.Listen();
        epoller_.Create();
        AddConnection(listensock_.Fd(), EPOLLIN | EPOLLET);
        logMessage(Debug, "init server success");
    }

    // 事件派发器
    int Dispatcher() // 名字要改
    {
        int timeout = -1;
        while (true)
        {
            LoopOnce(timeout);
        }
    }

    void LoopOnce(int timeout)
    {
        int n = epoller_.Wait(revs_, gnum, timeout);
        for (int i = 0; i < n; i++)
        {
            int fd = revs_[i].data.fd;
            uint32_t events = revs_[i].events;
            logMessage(Debug, "当前正在处理%d上的%s", fd, (events & EPOLLIN) ? "EPOLLIN" : "OTHER");
            // 我们将所有的异常情况，最后全部转化成为recv，send的异常！
            if ((events & EPOLLERR) || (events & EPOLLHUP))
                events |= (EPOLLIN | EPOLLOUT);

            if ((events & EPOLLIN) && ConnIsExists(fd))
                connections_[fd]->recver_(connections_[fd]);
            if ((events & EPOLLOUT) && ConnIsExists(fd))
                connections_[fd]->sender_(connections_[fd]);
        }
    }

    void AddConnection(int fd, uint32_t events, std::string ip = "127.0.0.1", uint16_t port = gport)
    {
        // 1. 设置fd是非阻塞
        if (events & EPOLLET)
            Util::SetNonBlock(fd);
        // 2. 构建connection对象，交给connections_来进行管理
        Connection *conn = new Connection(fd, ip, port);
        if (fd == listensock_.Fd())
        {
            conn->Register(std::bind(&EpollServer::Accepter, this, std::placeholders::_1), nullptr, nullptr);
        }
        else
        {
            conn->Register(std::bind(&EpollServer::Recver, this, std::placeholders::_1),
                           std::bind(&EpollServer::Sender, this, std::placeholders::_1),
                           std::bind(&EpollServer::Excepter, this, std::placeholders::_1));
        }
        conn->events = events;
        conn->R = this;
        connections_.insert(std::pair<int, Connection *>(fd, conn));
        // 3. fd && events 写透到内核中
        bool r = epoller_.AddModEvent(fd, events, EPOLL_CTL_ADD);
        assert(r);
        (void)r;

        logMessage(Debug, "AddConnection success, fd: %d, clientinfo: [%s:%d]", fd, ip.c_str(), port);
    }
    bool EnableReadWrite(Connection *conn, bool readable, bool writeable)
    {
        conn->events =((readable ? EPOLLIN : 0) | (writeable ? EPOLLOUT : 0) | EPOLLET);
        return epoller_.AddModEvent(conn->fd_, conn->events, EPOLL_CTL_MOD);
    }
    // 连接管理器
    void Accepter(Connection *conn)
    {
        do
        {
            int err = 0;
            // 1. 新连接到来
            // logMessage(Debug, "get a new link ...");
            std::string clientip;
            uint16_t clientport;
            int sock = listensock_.Accept(&clientip, &clientport, &err);
            if (sock > 0)
            {
                logMessage(Debug, "%s:%d 已经连上了服务器了", clientip.c_str(), clientport);
                // 1.1 此时在这里，我们能不能进行recv/read ? 不能，只有epoll知道sock上面的事件情况，将sock添加到epoll中
                AddConnection(sock, EPOLLIN | EPOLLET, clientip, clientport);
            }
            else
            {
                if (err == EAGAIN || err == EWOULDBLOCK)
                    break;
                else if (err == EINTR)
                    continue;
                else
                {
                    logMessage(Warning, "errstring : %s, errcode: %d", strerror(err), err);
                    continue;
                }
            }
        } while (conn->events & EPOLLET);

        logMessage(Debug, "accepter done ...");
    }

    void Recver(Connection *conn)
    {
        // 读取完毕本轮数据！
        do
        {
            char buffer[bsize];
            ssize_t n = recv(conn->fd_, buffer, sizeof(buffer) - 1, 0);
            if (n > 0)
            {
                buffer[n] = 0;
                conn->inbuffer_ += buffer;
                // 根据基本协议，进行数据分析 -- 自己定过一个！
                std::string requestStr;
                int n = protocol_ns::ParsePackage(conn->inbuffer_, &requestStr);
                if(n > 0) 
                {
                    requestStr = protocol_ns::RemoveHeader(requestStr, n);
                    Request req;
                    req.Deserialize(requestStr);
                    func_(conn, req); // request 保证是一个完整的请求报文！
                }

                // logMessage(Debug, "inbuffer: %s, [%d]", conn->inbuffer_.c_str(), conn->fd_);
            }
            else if (n == 0)
            {
                conn->excepter_(conn);
                break;
            }
            else
            {
                if (errno == EAGAIN || errno == EWOULDBLOCK)
                    break;
                else if (errno == EINTR)
                    continue;
                else{
                    conn->excepter_(conn);
                    break;
                }
            }
        } while (conn->events & EPOLLET);
    }

    void Sender(Connection *conn)
    {
        do
        {
            ssize_t n = send(conn->fd_, conn->outbuffer_.c_str(), conn->outbuffer_.size(), 0);
            if(n > 0)
            {
                conn->outbuffer_.erase(0, n);
                if(conn->outbuffer_.empty()) 
                {
                    EnableReadWrite(conn, true, false);
                    break;
                }
                else
                {
                    EnableReadWrite(conn, true, true);
                }
            }
            else
            {
                if(errno == EAGAIN || errno == EWOULDBLOCK) break;
                else if(errno == EINTR) continue;
                else
                {
                    conn->excepter_(conn);
                    break;
                }
            }
        }while(conn->events & EPOLLET);
    }

    void Excepter(Connection *conn)
    {
        logMessage(Debug, "Excepter..., fd: %d, clientinfo: [%s:%d]", conn->fd_, conn->clientip_.c_str(), conn->clientport_);
    }

    bool ConnIsExists(int fd)
    {
        return connections_.find(fd) != connections_.end();
    }
    ~EpollServer()
    {
        listensock_.Close();
        epoller_.Close();
    }

private:
    uint16_t port_;
    Sock listensock_;
    Epoller epoller_;
    struct epoll_event revs_[gnum];
    func_t func_;
    std::unordered_map<int, Connection *> connections_;
};